<template>
  <div :class="classes">
    <button type="button" :class="arrowClasses" class="left" @click="arrowEvent(-1)">
      <b-icon name="ios-arrow-back"></b-icon>
    </button>
    <div :class="[prefixCls + '-list']">
      <div :class="[prefixCls + '-track', showCopyTrack ? '' : 'higher']" :style="trackStyles" ref="originTrack">
        <slot></slot>
      </div>
      <div :class="[prefixCls + '-track', showCopyTrack ? 'higher' : '']" :style="copyTrackStyles" ref="copyTrack"
           v-if="loop">
      </div>
    </div>
    <button type="button" :class="arrowClasses" class="right" @click="arrowEvent(1)">
      <b-icon name="ios-arrow-forward"></b-icon>
    </button>
    <ul :class="dotsClasses">
      <template v-for="n in slides.length">
        <li :class="[n - 1 === currentIndex ? prefixCls + '-active' : '']" :key="n"
            @click="dotsEvent('click', n - 1)"
            @mouseover="dotsEvent('hover', n - 1)">
          <button type="button" :class="[radiusDot ? 'radius' : '']"></button>
        </li>
      </template>
    </ul>
  </div>
</template>
<script>
  import { oneOf } from '../../utils/util'
  import { on, off, getStyle } from '../../utils/dom'

  const prefixCls = 'bin-carousel'

  export default {
    name: 'BCarousel',
    props: {
      arrow: {
        type: String,
        default: 'hover',
        validator (value) {
          return oneOf(value, ['hover', 'always', 'never'])
        }
      },
      autoplay: {
        type: Boolean,
        default: false
      },
      autoplaySpeed: {
        type: Number,
        default: 2000
      },
      loop: {
        type: Boolean,
        default: false
      },
      easing: {
        type: String,
        default: 'ease'
      },
      dots: {
        type: String,
        default: 'inside',
        validator (value) {
          return oneOf(value, ['inside', 'outside', 'none'])
        }
      },
      radiusDot: {
        type: Boolean,
        default: false
      },
      trigger: {
        type: String,
        default: 'click',
        validator (value) {
          return oneOf(value, ['click', 'hover'])
        }
      },
      value: {
        type: Number,
        default: 0
      },
      height: {
        type: [String, Number],
        default: 'auto',
        validator (value) {
          return value === 'auto' || Object.prototype.toString.call(value) === '[object Number]'
        }
      }
    },
    data () {
      return {
        prefixCls: prefixCls,
        listWidth: 0,
        trackWidth: 0,
        trackOffset: 0,
        trackCopyOffset: 0,
        showCopyTrack: false,
        slides: [],
        slideInstances: [],
        timer: null,
        ready: false,
        currentIndex: this.value,
        trackIndex: this.value,
        copyTrackIndex: this.value,
        hideTrackPos: -1 // 默认左滑
      }
    },
    computed: {
      classes () {
        return [
          `${prefixCls}`
        ]
      },
      trackStyles () {
        return {
          width: `${this.trackWidth}px`,
          transform: `translate3d(${-this.trackOffset}px, 0px, 0px)`,
          transition: `transform 500ms ${this.easing}`
        }
      },
      copyTrackStyles () {
        return {
          width: `${this.trackWidth}px`,
          transform: `translate3d(${-this.trackCopyOffset}px, 0px, 0px)`,
          transition: `transform 500ms ${this.easing}`,
          position: 'absolute',
          top: 0
        }
      },
      arrowClasses () {
        return [
          `${prefixCls}-arrow`,
          `${prefixCls}-arrow-${this.arrow}`
        ]
      },
      dotsClasses () {
        return [
          `${prefixCls}-dots`,
          `${prefixCls}-dots-${this.dots}`
        ]
      }
    },
    methods: {
      // find option component
      findChild (cb) {
        const find = function (child) {
          const name = child.$options.componentName

          if (name) {
            cb(child)
          } else if (child.$children.length) {
            child.$children.forEach((innerChild) => {
              find(innerChild, cb)
            })
          }
        }

        if (this.slideInstances.length || !this.$children) {
          this.slideInstances.forEach((child) => {
            find(child)
          })
        } else {
          this.$children.forEach((child) => {
            find(child)
          })
        }
      },
      // copy trackDom
      initCopyTrackDom () {
        this.$nextTick(() => {
          this.$refs.copyTrack.innerHTML = this.$refs.originTrack.innerHTML
        })
      },
      updateSlides (init) {
        let slides = []
        let index = 1

        this.findChild((child) => {
          slides.push({
            $el: child.$el
          })
          child.index = index++

          if (init) {
            this.slideInstances.push(child)
          }
        })

        this.slides = slides
        this.updatePos()
      },
      updatePos () {
        this.findChild((child) => {
          child.width = this.listWidth
          child.height = typeof this.height === 'number' ? `${this.height}px` : this.height
        })

        this.trackWidth = (this.slides.length || 0) * this.listWidth
      },
      // use when slot changed
      slotChange () {
        this.$nextTick(() => {
          this.slides = []
          this.slideInstances = []

          this.updateSlides(true, true)
          this.updatePos()
          this.updateOffset()
        })
      },
      handleResize () {
        this.listWidth = parseInt(getStyle(this.$el, 'width'))
        this.updatePos()
        this.updateOffset()
      },
      updateTrackPos (index) {
        if (this.showCopyTrack) {
          this.trackIndex = index
        } else {
          this.copyTrackIndex = index
        }
      },
      updateTrackIndex (index) {
        if (this.showCopyTrack) {
          this.copyTrackIndex = index
        } else {
          this.trackIndex = index
        }
        this.currentIndex = index
      },
      add (offset) {
        // 获取单个轨道的图片数
        let slidesLen = this.slides.length
        // 如果是无缝滚动，需要初始化双轨道位置
        if (this.loop) {
          if (offset > 0) {
            // 初始化左滑轨道位置
            this.hideTrackPos = -1
          } else {
            // 初始化右滑轨道位置
            this.hideTrackPos = slidesLen
          }
          this.updateTrackPos(this.hideTrackPos)
        }
        // 获取当前展示图片的索引值
        const oldIndex = this.showCopyTrack ? this.copyTrackIndex : this.trackIndex
        let index = oldIndex + offset
        while (index < 0) index += slidesLen
        if (((offset > 0 && index === slidesLen) || (offset < 0 && index === slidesLen - 1)) && this.loop) {
          // 极限值（左滑：当前索引为总图片张数， 右滑：当前索引为总图片张数 - 1）切换轨道
          this.showCopyTrack = !this.showCopyTrack
          this.trackIndex += offset
          this.copyTrackIndex += offset
        } else {
          if (!this.loop) index = index % this.slides.length
          this.updateTrackIndex(index)
        }
        this.currentIndex = index === this.slides.length ? 0 : index
        this.$emit('on-change', oldIndex, this.currentIndex)
        this.$emit('input', this.currentIndex)
      },
      arrowEvent (offset) {
        this.setAutoplay()
        this.add(offset)
      },
      dotsEvent (event, n) {
        let curIndex = this.showCopyTrack ? this.copyTrackIndex : this.trackIndex
        if (event === this.trigger && curIndex !== n) {
          this.updateTrackIndex(n)
          this.$emit('input', n)
          // Reset autoplay timer when trigger be activated
          this.setAutoplay()
        }
      },
      setAutoplay () {
        window.clearInterval(this.timer)
        if (this.autoplay) {
          this.timer = window.setInterval(() => {
            this.add(1)
          }, this.autoplaySpeed)
        }
      },
      updateOffset () {
        this.$nextTick(() => {
          /* hack: revise copyTrack offset (1px) */
          let ofs = this.copyTrackIndex > 0 ? -1 : 1
          this.trackOffset = this.trackIndex * this.listWidth
          this.trackCopyOffset = this.copyTrackIndex * this.listWidth + ofs
        })
      }
    },
    watch: {
      autoplay () {
        this.setAutoplay()
      },
      autoplaySpeed () {
        this.setAutoplay()
      },
      trackIndex () {
        this.updateOffset()
      },
      copyTrackIndex () {
        this.updateOffset()
      },
      height () {
        this.updatePos()
      },
      value (val) {
//                this.currentIndex = val;
//                this.trackIndex = val;
        this.updateTrackIndex(val)
        this.setAutoplay()
      }
    },
    mounted () {
      this.updateSlides(true)
      this.handleResize()
      this.setAutoplay()
//            window.addEventListener('resize', this.handleResize, false);
      on(window, 'resize', this.handleResize)
    },
    beforeDestroy () {
//            window.removeEventListener('resize', this.handleResize, false);
      off(window, 'resize', this.handleResize)
    }
  }
</script>
